import Singleton from "./Singleton";

class Timer {

    public reset() {
        this._elapsedTime = 0;
    }

    public setData(interval:number, removeWhenTrigger:boolean) {
        this._interval = interval;
        this._removeWhenTrigger = removeWhenTrigger;
    }

    public constructor(interval:number, removeWhenTrigger=false) {
        this.setData(interval, removeWhenTrigger);
    }

    public trigger(dt:number):boolean {
        if (this._interval <= 0) return false;

        this._elapsedTime += dt;

        if (this._elapsedTime >= this._interval) {
            this._elapsedTime -= this._interval;
            return true;
        }
        return false;
    }

    public canRemove():boolean {
        return this._removeWhenTrigger;
    }

//private
    private _interval = 0;
    private _removeWhenTrigger = false;
    private _elapsedTime = 0;
}

interface ITimerData {
    id:number;
    timer:Timer;
    priority:number;
    callback:()=>void;
}

class TimerMgr extends Singleton {

    /**
     *
     * @param delta
     * @param callback
     * @param priority 默认0；优先级越大越优先执行。优先级相同则先加入先执行。
     * @returns
     */
    public delay(delta:number, callback:()=>void, priority=0):number {
        let id = ++this._timerId;
        let timer = this.getTimer(delta, true);
        this.insertTimerData({id, timer, priority, callback});
        return id;
    }

    public interval(delta:number, callback:()=>void, priority=0):number {
        let id = ++this._timerId;
        let timer = this.getTimer(delta, false);
        this.insertTimerData({id, timer, priority, callback});
        return id;
    }

    public clear(id:number):boolean {
        for (let l = this._timerArr.length, i = l - 1; i >= 0; --i) {
            let td = this._timerArr[i];
            if (td.id == id) {
                this._timerArr.splice(i, 1);
                this._timerMap.delete(id);
                this._pool.push(td.timer);
                return true;
            }
        }
        return false;
    }

    public update(dt:number) {
        let len = this._timerArr.length;
        if (len > 0) {
            for (let i = len-1; i >= 0; --i) {
                let td = this._timerArr[i],
                    timer = td.timer;
                if (timer.trigger(dt)) {
                    if (timer.canRemove()) {
                        this._timerArr.splice(i, 1);
                        this._timerMap.delete(td.id);
                        this._pool.push(timer);
                    }
                    td.callback && td.callback();
                }
            }
        }
    }
////
    private getTimer(delta:number, removeWhenTrigger=false):Timer {
        if (this._pool.length > 0) {
            let t = this._pool.shift()!;
            t.reset();
            t.setData(delta, removeWhenTrigger);
            return t;
        }
        return new Timer(delta, removeWhenTrigger);
    }

    // 按 priority 从大到小排列
    private insertTimerData(td:ITimerData) {
        let len = this._timerArr.length;
        let position = 0;
        for (let i = len-1; i >= 0; --i) {
            let _td = this._timerArr[i];
            if (td.priority <= _td.priority) {
                position = len + 1;
                break;
            }
        }
        this._timerArr.splice(position, 0, td);
        this._timerMap.set(td.id, td);
    }
//private
    private _timerId = 0;
    private _timerArr = new Array<ITimerData>();
    private _timerMap = new Map<number, ITimerData>();
    private _pool:Timer[] = [];
}

const Timers = TimerMgr.getInstance() as TimerMgr
export default Timers;